/*
 * @(#)XfdmPseudoRule.java        1.0 2000/05/09
 *
 * This file is part of Xfuzzy 3.0, a design environment for fuzzy logic
 * based systems.
 *
 * (c) 2000 IMSE-CNM. The authors may be contacted by the email address:
 *                    xfuzzy-team@imse.cnm.es
 *
 * Xfuzzy is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation.
 *
 * Xfuzzy is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * for more details.
 */

package xfuzzy.xfdm.model.algorithm;

import xfuzzy.lang.*;

/**
 * Informaci�n necesaria para crear una regla en el algoritmo de Wang y Mendel
 * y derivados
 * 
 * @author Francisco Jos� Moreno Velo
 *
 */
public class XfdmPseudoRule {

	//----------------------------------------------------------------------------//
	//                            MIEMBROS PRIVADOS                               //
	//----------------------------------------------------------------------------//

	/**
	 * �ndices de las funciones de pertenencia de cada variable de entrada
	 */
	private int[] antecedent;
	
	/**
	 * Grados de activaci�n de cada clase (en caso de clasificaci�n)
	 */
	private double[][] class_degree;
	
	/**
	 * Valor medio de cada variable de salida
	 */
	private double[] center;
	
	/**
	 * Grado de activaci�n acumulado
	 */
	private double degree;

	//----------------------------------------------------------------------------//
	//                               CONSTRUCTOR                                  //
	//----------------------------------------------------------------------------//

	/**
	 * Constructor de una regla tipo controlador
	 */
	public XfdmPseudoRule(int[] antecedent, int numvar) {
		this.antecedent = antecedent;
		this.center = new double[numvar];
		this.degree = 0;
		this.class_degree = null;
	}

	/**
	 * Constructor de una regla tipo clasificador
	 */
	public XfdmPseudoRule(int[] antecedent, int[] numclass) {
		this.antecedent = antecedent;
		this.class_degree = new double[numclass.length][];
		for(int i=0; i<numclass.length; i++) {
			this.class_degree[i] = new double[numclass[i]];
		}
		this.center = null;
		this.degree = 0;
	}

	//----------------------------------------------------------------------------//
	//                             M�TODOS P�BLICOS                               //
	//----------------------------------------------------------------------------//

	/**
	 * Verifica si la lista coincide con el antecedente de la regla
	 */
	public boolean test(int[] ant) {
		if(antecedent.length != ant.length) return false;
		for(int i=0; i<ant.length; i++) if(antecedent[i] != ant[i]) return false;
		return true;
	}

	/**
	 * Actualiza el valor del consecuente (controlador)	
	 */
	public void update(double nc[], double nd) {
		for(int i=0; i<center.length; i++) {
			center[i] = (degree*center[i] + nd*nc[i])/(degree+nd);
		}
		degree += nd;
	}

	/**
	 * Actualiza el valor del consecuente (clasificador)
	 */
	public void update(int[] index, double degree) {
		for(int i=0; i<class_degree.length; i++) {
			this.class_degree[i][index[i]] += degree;
		}
	}

	/**
	 * Obtiene la lista de �ndices del antecedente
	 */
	public int[] getAntecedent() {
		return antecedent;
	}

	/**
	 * Obtiene el �ndice de la clase con mayor grado de activaci�n
	 */
	public int[] getClassIndexes() {
		double max[] = new double[class_degree.length];
		int index[] = new int[class_degree.length];
		for(int v=0; v<class_degree.length; v++) {
			for(int i=0; i<class_degree[v].length; i++) {
				if(class_degree[v][i]>=max[v]) { index[v] = i; max[v] = class_degree[v][i];}
			}
		}
		return index;
	}

	/**
	 * Obtiene la eficiencia de la regla
	 */
	public double getPerformance() {
		int selected[] = getClassIndexes();
		double performance = 0;
		for(int v=0; v<class_degree.length; v++) {
			for(int i=0; i<class_degree[v].length; i++) {
				if(i==selected[v]) performance += class_degree[v][i];
				else performance -= class_degree[v][i];
			}
		}
		return performance/class_degree.length;
	}

	/**
	 * Obtiene el valor del centro de las MF de salida
	 */
	public double[] getCenter() {
		return this.center;
	}

	/**
	 * Obtiene el valor del grado de activaci�n acumulado
	 */
	public double getDegree() {
		return this.degree;
	}

	/**
	 * Genera el antecedente de la regla
	 */
	public Relation createAntecedent(KnowledgeBase base) {
		Variable ivar[] = base.getInputs();
		int is = Relation.IS;
		int index[] = this.antecedent;

		LinguisticLabel pmf[][] = new LinguisticLabel[index.length][];
		for(int i=0; i<index.length; i++) {
			pmf[i] = ivar[i].getType().getAllMembershipFunctions();
		}

		Relation rel = Relation.create(is,null,null,ivar[0],pmf[0][index[0]],base);
		for(int j=1; j<index.length; j++) {
			Relation nrel = Relation.create(is,null,null,ivar[j],pmf[j][index[j]],base);
			rel = Relation.create(Relation.AND,rel,nrel,null,null,base);
		}

		return rel;
	}
}
